# coding=utf-8
from tkinter import messagebox, Tk, StringVar, Button, Menu, Label, Entry, Canvas
import psutil
from PIL import ImageTk, Image
from shutil import copyfile
from os import error, system, path, remove, getcwd, mkdir, startfile
import configparser
import logging
import requests


def check_dir(dirs):
    if not path.isdir(dirs):
        try:
            mkdir(dirs)
        except IOError:
            messagebox.showerror(title='文件夹创建', message='无法创建文件夹[ %s ]' % dirs)
log_dir = getcwd() + '\log'
log_file = log_dir + '\Logs.ini'
config_dir = getcwd() + '\config'
check_dir(dirs=log_dir)
check_dir(dirs=config_dir)

logger = logging.getLogger()
logger.setLevel('DEBUG')
BASIC_FORMAT = "%(asctime)s :%(levelname)s :%(message)s"
DATE_FORMAT = '%Y-%m-%d %H:%M:%S'
formatter = logging.Formatter(BASIC_FORMAT, DATE_FORMAT)
fhlr = logging.FileHandler(log_file)  # 输出到文件的handler
fhlr.setFormatter(formatter)
logger.addHandler(fhlr)
logger.info('打开主程序')
#####开始检测/生成配置文件
init_config_str_programme = str('''####----------------部分参数解析
#[Intranet]        这个不能改,反正别改完事了,如果改了[ ](所有）里面的内容就会报错,别问为啥，问就是我技术不行
#fg                字体背景色--->字体参考: https://www.5tu.cn/colors/yansezhongwenming.html
#bg                按钮背景色
#fonts             按钮采用的字体
#fontsize          按钮的文字大小(整数)
#activebackground  按钮背景色

[Intranet]
name=内网
#这是按钮配置
activebackground=blue
fg=Lavender
bg=Silver
fonts=楷体
fontsize=14

#这是方案配置
IP=172.31.0.127
GATEWAY=172.31.0.254
NETMASK=255.255.255.0
DNS1=172.31.0.254
DNS2=119.29.29.29



[Extranet]
name=外网
#这是按钮配置
activebackground=blue
fg=Lavender
bg=Silver
fonts=楷体
fontsize=14

#这是方案配置
IP=10.0.9.123
GATEWAY=10.0.9.254
NETMASK=255.255.255.0
DNS1=119.29.29.29
DNS2=114.114.114.114


[customize1]
name=网络1
#这是按钮配置
activebackground=blue
fg=Lavender
bg=Silver
fonts=楷体
fontsize=14

#这是方案配置
IP=10.1.0.11
GATEWAY=10.1.0.254
NETMASK=255.255.255.0
DNS1=119.29.29.29
DNS2=114.114.114.114

[customize2]
name=网络2
#这是按钮配置
activebackground=blue
fg=Lavender
bg=Silver
fonts=楷体
fontsize=14

#这是方案配置
IP=10.2.0.11
GATEWAY=10.2.0.254
NETMASK=255.255.255.0
DNS1=119.29.29.29
DNS2=114.114.114.114
''')


class init_config:
    def __init__(self):
        self.dir = getcwd() + '\config'
        self.log = getcwd() + '\log'
        dir = [self.dir, self.log]
        for i in dir:
            if not path.isdir(i):
                try:
                    mkdir(self.dir)
                    logger.info('文件夹[ %s ]创建成功' %i)
                except IOError:
                    info = ('文件夹[  ]创建失败，建议使用管理员运行' %i)
                    logger.info(info)
                    messagebox.showerror(title='程序初始化', message=info)

    def get_size(self, file):
        '''
        :param File_Size: 返回文件大小(MB)
        :return:
        '''
        size = path.getsize(file)
        mb = size / 1024 / 1024
        return mb
    def get_Net_list(self):
        """获取ipv4地址"""
        net_info = psutil.net_if_addrs()
        ipv4_list = []
        net_list = {}
        id = int(0)
        for card in net_info:
            if card != '蓝牙网络连接' and card != 'Loopback Pseudo-Interface 1':  #从结果剔除两个名称的网卡
                id = id + int(1)  #ID递增+1
                snicList = net_info[card]
                for snic in snicList:
                    if snic.family.name == 'AF_INET':
                        ipv4 = snic.address
                        ipv4_list.append(ipv4)
                        net_list.update({id: {card: ipv4}})
        if len(ipv4_list) >= 1:
            logger.info('获取网卡信息成功')
            return net_list
        else:
            messagebox.showerror(title='获取网卡', message='获取网卡信息失败,请查看日志获取帮助')
            logger.error('网卡信息获取失败，请检查网卡名称是否设置为: 蓝牙网络连接、Loopback Pseudo-Interface 1,如果是请改名')
            return 'None'

    def logs(self):
        file = self.log + '\Logs.ini'
        if path.isfile(file):
            try:
                f = open(file, 'w', encoding='gb2312')
                f.write('')
                f.close()
                logger.info('清空日志成功')
                messagebox.showinfo(title='重置日志', message='重置日志成功')
            except IOError:
                messagebox.showerror(title='重置日志', message='重置日志失败,请查看日志')
                logger.error('请手动删除文件--->[ %s ]' %file)
        else:
            remove(file)
            messagebox.showinfo(title='重置日志', message='找不到日志文件')
            logger.error('找不到日志文件')
    def programme(self, file):
        file = self.dir + file
        if not path.isfile(file):
            try:
                # 写入方案参数
                rw_menu = open(file, 'w', encoding='gb2312')
                rw_menu.write(init_config_str_programme)
                rw_menu.close()
                logger.info('方案初始化成功')
            except IOError:
                messagebox.showinfo(title='程序初始化', message='初始化失败，请重新运行')
                logger.error('方案初始化失败')
        return file
    def check(self, file, n, name, mode=None):
        '''

        :param file: 检测的文件
        :param n: sections数
        :return:
        '''
        init = init_config()
        init.programme(file='\programme.ini')
        ##开始检测配置文件
        config = configparser.ConfigParser()
        config.read(file, encoding="gb2312")
        bt_list = []
        i = int(0)
        for u in config.sections():
            bt_list.append(u)
            i = int(i + 1)
        if i != n:
            init = messagebox.askyesno(title='配置检测', message='文件[ %s ]检测到内容有误，是否重置' %name)
            if init:
                bak = file + '.bak'
                if not path.isfile(bak):
                    copyfile(file, bak)
                remove(file)
                messagebox.showinfo(title='重置成功', message='请重新运行软件')
            else:
                messagebox.showwarning(title='配置检测', message='请重新修改配置文件')
        else:
            if mode:
                if mode == 'init':
                    remove(file)
                    try:
                        # 写入按钮参数
                        rw_bt = open(file, 'w', encoding='gb2312')
                        rw_bt.write(init_config_str_programme)
                        rw_bt.close()
                        ##添加按钮
                        bt = Buttons(windows=Windows)
                        # 添加功能按钮
                        bt.fun()
                        messagebox.showinfo(title='按钮初始化成功', message=file)
                        logger.info('按钮初始化成功')
                    except IOError:
                        messagebox.showerror(title='按钮初始化失败', message=file)
                        logger.info('按钮初始化失败')
                elif mode == 'update':
                    ##添加按钮
                    bt = Buttons(windows=Windows)
                    # 添加功能按钮
                    bt.fun()
                messagebox.showinfo(title=name, message='[ %s ]成功' %name)
    def get_images(self, url, file, name):
        try:
            image_get = requests.get(url)
            image_get.raise_for_status()
            image_get.encoding = image_get.encoding
            with open(file, 'wb') as f:
                f.write(image_get.content)
                f.close()
            logger.info('%s获取成功' %name)
            return int(0)
        except IOError:
            messagebox.showerror('请联网获取%s..' %name)
            logger.error('%s获取失败,请将一个jpg图片命名为[ %s ],然后放到程序目录下的[ config ]文件夹下' %(name, file))

init = init_config()
pr_file = init.programme(file='\programme.ini')
card_file = config_dir + '\card_list.ini'
log_size = init.get_size(file=log_file)
if log_size >= 1:
    remove(log_size)
    logger.info('日志文件大于1MB,已清除旧记录')

'''
开始创建程序窗口
'''
Windows = Tk()


# 设置宽、高、左、上
Windows.geometry('600x374+400+200')
Windows.title('IP易换')




header = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'}
ico_file = './config/Y.ico'
if not path.isfile(ico_file):
    init.get_images(url='https://gitee.com/faith01238/IP/attach_files/602457/download/Y.ico',
                file=ico_file, name='程序图标')
else:
    Windows.iconbitmap(ico_file)
# Windows.resizable()



bg = "./config/item.jpg"
if not path.isfile(bg):
    init.get_images(url='https://gitee.com/faith01238/IP/attach_files/602527/download/item.jpg',
                    file=bg, name='背景图')
if path.isfile(bg):
    image = Image.open(bg)
    im = ImageTk.PhotoImage(image)
    canvas = Canvas(Windows, width=600,  # 指定Canvas组件的宽度
                            height=384,  # 指定Canvas组件的高度
                            bg='white')  # 指定Canvas组件的背景色
    canvas.create_image(300, 180, image=im)  # 使用create_image将图片添加到Canvas组件中
    canvas.create_text(320, 75,  # 使用create_text方法在坐标（302，77）处绘制文字
                       text='在下方输入网卡ID(默认: 1)',  # 所绘制文字的内容
                       fill='blue')  # 所绘制文字的颜色为灰色
    canvas.pack()  # 将Canvas添加到主窗口
#添加输入框
id = StringVar()
id.set('在这输入网卡的ID，默认ID:1')
id_entry = Entry(Windows, textvariable='你猜')
id_entry.place(x=250, y=90, width=300, height=30)

'''
这是实现IP切换的类
'''

class command:
    def __init__(self):
        i = self

    def init_dhcp(self, card):
        dhcp_cmd = "netsh interface ipv4 set address name=\"" + card + "\" source=dhcp"
        try:
            system(dhcp_cmd)
            messagebox.showinfo(title='执行结果', message='对[ %s ]配置[ DHCP ]成功' %card)
            logger.info('对 [ %s ]网卡配置[ DHCP ]模式成功--->[ %s ]' % (card, dhcp_cmd))
        except RuntimeError:
            messagebox.showerror(title='执行结果', message='对[ %s ]配置[ DHCP ]失败' %card)
            logger.info('对 [ %s ]网卡配置[ DHCP ]模式失败--->[ %s ]' % (card, dhcp_cmd))
        update_menu()

    def config(self, card, ip, netmask, gateway, dns1, dns2, name):
        dhcp_cmd = "netsh interface ipv4 set address name=\"" + card + "\" source=dhcp"
        ip_cmd = "netsh interface ip set address name=\"" + card + "\" source=static address=" + ip + " mask=" + netmask + " gateway=" + gateway
        dns_cmd = "netsh interface ipv4 set dnsservers name=\"" + card + "\" source=static address=" + dns1 + " register=primary validate=no"
        dns2_cmd = "netsh interface ipv4 add dnsservers  name=\"" + card + "\" " + dns2 + " index=2 validate=no"
        try:
            logger.info("正在对[ %s ]清除配置-->[ %s ]" % (card, dhcp_cmd))
            system(dhcp_cmd)

            logger.info("正在对[ %s ]设置IP--->[ %s ]" % (card, ip_cmd))
            system(ip_cmd)

            logger.info("正在对[ %s ]设置DNS1-->[ %s ]" % (card, dns_cmd))
            system(dns_cmd)

            logger.info("正在对[ %s ]设置DNS2-->[ %s ]" % (card, dns2_cmd))
            system(dns2_cmd)

            update_menu()
            messagebox.showinfo(title='配置结果', message='配置网卡[ %s ]-[ %s ]模式成功' % (card, name))
        except error:
            messagebox.showerror("请检查本机是否存在此IP[ %s ]" % ip)
            logger.error("请检查本机是否存在此IP[ %s ]" % ip)
        update_menu()
#开始切换
def switch(id, name=None):
    cmd = command()
    ka_list = init.get_Net_list()
    logger.info('当前函数:[ switch ],获取到的ID是: [ %s ]' % id)
    ####从输入框获取ID
    card_id = id_entry.get() #获取输入框内容
    if not card_id:
        card_id = int(1)
    if not int(card_id):
        messagebox.showerror(title='输入错误', message='请输入整数')
        logger.error('只能输入整数作为网卡ID')
        card_id = int(len(ka_list)) + 1
    if int(card_id) > int(len(ka_list)):
        messagebox.showerror(title='网卡ID错误', message='你没有那么多网卡')
        logger.error('网卡ID不能大于实际网卡数量,当前设置[ %s ]' %card_id)
    else:
        if int(card_id) < int(1):
            logger.error('网卡ID输入错误，当前输入ID[ %s ]小于1' %card_id)
            messagebox.showerror(title='网卡ID错误', message='网卡ID最小值: 1,请重新输入')
        else:
            ka = ka_list[int(card_id)]
            for card in ka:
                logger.info('已选择网卡: [ %s ]' %card)
                if name == 'dhcp':
                    cmd.init_dhcp(card=card)
                else:
                    #从按钮获取方法
                    net_config = configparser.ConfigParser()
                    net_config.read(pr_file, encoding='gb2312')
                    NAME = net_config.get(id, "name")
                    IP = net_config.get(id, "IP")
                    ip_list = init.get_Net_list()
                    ips = ip_list[int(card_id)][card]
                    print(str(IP), str(ips))
                    if str(IP) == str(ips):
                        messagebox.showwarning(title='IP切换', message='本机已存在此IP,请修改方案或者修改当前IP')
                    else:
                        GATEWAY = net_config.get(id, "GATEWAY")
                        NETMASK = net_config.get(id, "NETMASK")
                        DNS1 = net_config.get(id, "DNS1")
                        DNS2 = net_config.get(id, "DNS2")
                        INFO = ("cmd.config(card=%s, ip=%s, gateway=%s, netmask=%s, dns2=%s, dns1=%s, name=%s)" %(card, IP, GATEWAY, NETMASK, DNS2, DNS1, NAME))
                        print(INFO)
                        cmd.config(card=card, ip=IP, gateway=GATEWAY, netmask=NETMASK, dns2=DNS2, dns1=DNS1, name=NAME)

class Buttons:
    def __init__(self, windows):
        self.windows = windows
    '''
    下面是按钮的实现
    activebackground  当鼠标放上去时，按钮的背景色
    bd  按钮边框的大小，默认为 2 个像素
    bg  按钮的背景色
    height 按钮的高度
    image  按钮上要显示的图片
    width  按钮的宽度，如未设置此项，其大小以适应按钮的内容（文本或图片的大小）
    '''
    def fun(self):
        width, height, y = 10, 1, 140
        config_pr = configparser.ConfigParser()
        config_pr.read(pr_file, encoding="gb2312")

        name = config_pr.get('Intranet', 'name')
        fonts = config_pr.get('Intranet', 'fonts')
        fontsize = config_pr.get('Intranet', 'fontsize')
        active = config_pr.get('Intranet', 'activebackground')
        fgs = config_pr.get('Intranet', 'fg')
        bgs = config_pr.get('Intranet', 'bg')
        #字体颜色配置来自教程-->https://zhuanlan.zhihu.com/p/145912028
        Intranet = Button(self.windows, text=name, width=width, height=height,
                         font=(fonts, int(fontsize), 'bold'), activebackground=active,
                         fg=fgs, #字体背景
                         bg=bgs, #按钮背景
                         command=lambda :switch(id='Intranet')
                         )
        Intranet.pack()  # 加载到窗体
        Intranet.place(x=int(20), y=int(y))  # 设置坐标（以程序界面大小为基准)

        mode = str('Extranet')
        name = config_pr.get('Extranet', 'name')
        fonts = config_pr.get('Extranet', 'fonts')
        fontsize = config_pr.get('Extranet', 'fontsize')
        active = config_pr.get('Extranet', 'activebackground')
        fgs = config_pr.get('Extranet', 'fg')
        bgs = config_pr.get('Extranet', 'bg')
        #字体颜色配置来自教程-->https://zhuanlan.zhihu.com/p/145912028
        Extranet = Button(self.windows, text=name, width=width, height=height,
                         font=(fonts, int(fontsize), 'bold'), activebackground=active,
                         fg=fgs, #字体背景
                         bg=bgs, #按钮背景
                         command=lambda :switch(id='Extranet')
                         )
        Extranet.pack()  # 加载到窗体
        Extranet.place(x=int(170), y=int(y))  # 设置坐标（以程序界面大小为基准)


        name = config_pr.get('customize1', 'name')
        fonts = config_pr.get('customize1', 'fonts')
        fontsize = config_pr.get('customize1', 'fontsize')
        active = config_pr.get('customize1', 'activebackground')
        fgs = config_pr.get('customize1', 'fg')
        bgs = config_pr.get('customize1', 'bg')
        #字体颜色配置来自教程-->https://zhuanlan.zhihu.com/p/145912028
        customize1 = Button(self.windows, text=name, width=width, height=height,
                         font=(fonts, int(fontsize), 'bold'), activebackground=active,
                         fg=fgs, #字体背景
                         bg=bgs, #按钮背景
                         command=lambda :switch(id='customize1')
                         )
        customize1.pack()  # 加载到窗体
        customize1.place(x=int(320), y=int(y))  # 设置坐标（以程序界面大小为基准)

        name = config_pr.get('customize2', 'name')
        fonts = config_pr.get('customize2', 'fonts')
        fontsize = config_pr.get('customize2', 'fontsize')
        active = config_pr.get('customize2', 'activebackground')
        fgs = config_pr.get('customize2', 'fg')
        bgs = config_pr.get('customize2', 'bg')
        # 字体颜色配置来自教程-->https://zhuanlan.zhihu.com/p/145912028
        customize2 = Button(self.windows, text=name, width=width, height=height,
                            font=(fonts, int(fontsize), 'bold'), activebackground=active,
                            fg=fgs,  # 字体背景
                            bg=bgs,  # 按钮背景
                            command=lambda: switch(id='customize2')
                            )
        customize2.pack()  # 加载到窗体
        customize2.place(x=int(470), y=int(y))  # 设置坐标（以程序界面大小为基准)


        ####DHCP按钮
        bt_dhcp = Button(self.windows, text='DHCP模式', width=width, height=height,
                         font=('Fixdsys', 14, 'bold'), activebackground=active,
                         fg=fgs, #字体背景
                         bg=bgs, #按钮背景
                         command=lambda :switch(id='customize2', name='dhcp')
                         )
        bt_dhcp.pack()  # 加载到窗体
        bt_dhcp.place(x=int(100), y=int(210))  # 设置坐标（以程序界面大小为基准)


        ####DHCP按钮
        button1 = Button(self.windows, text='点击两次刷新网卡', width=14, height=height,
                         font=('Fixdsys', 14, 'bold'), activebackground=active,
                         fg=fgs, #字体背景
                         bg=bgs, #按钮背景
                         command=lambda :update_menu()
                         )
        button1.pack()  # 加载到窗体
        button1.place(x=int(380), y=int(210))  # 设置坐标（以程序界面大小为基准)

    def bt(self, name, width, height, font=('Fixdsys', 14, 'bold'), activebackground='blue', x=300, y=140):
        get_id = Button(self.windows, text=name, width=width, height=height, font=font, activebackground=activebackground)
        get_id.pack()
        get_id.place(x=int(x), y=int(y))
        return id

    def parts(self, name, url, x, y):
        go = Button(self.windows, text=name,
                    width=10, height=1, font=("楷体", 14, 'bold'),
                    activebackground='blue',
                    fg='blue',
                    bg='RosyBrown',
                    command=lambda :visit(url=url, name=name))
        go.pack()
        go.place(x=int(x), y=int(y))

'''
下面是实现菜单的功能
'''
def visit(url,name=None):
    import webbrowser
    webbrowser.open(url)
    logger.info('访问--->[ %s ]' %name)
def update(file):
    logger.info('修改文件[ %s ]' %file)
    startfile(file)
    ##添加按钮
    bt = Buttons(windows=Windows)
    # 刷新功能按钮
    bt.fun()


class add_menu:
    def __init__(self):
        # 新建一个菜单选项
        self.menu_file = Menu(Windows)
        # 新建[文件]菜单
        Windows.config(menu=self.menu_file)
        label = Label(Windows, text="")
        label.pack()
    def pr(self, name):
        config = Menu(self.menu_file, tearoff=False)
        config.add_separator()
        config.add_command(label='修改', command=lambda: startfile(pr_file))
        config.add_command(label='刷新', command=lambda: init.check(file=pr_file, n=4, name='刷新配置', mode='update'))
        config.add_command(label='初始化', command=lambda: init.check(file=pr_file, n=4, name='初始化', mode='init'))
        config.add_command(label='颜色单词对照', command=lambda :visit(url='https://www.5tu.cn/colors/yansezhongwenming.html'))
        self.menu_file.add_cascade(label=name, menu=config)
    def file(self, name):
        init_file = init_config()
        file = Menu(self.menu_file, tearoff=False)
        file.add_separator()
        file.add_command(label='打开配置文件目录', command=lambda: startfile(config_dir))
        file.add_command(label='查看程序运行日志', command=lambda: startfile(log_file))
        file.add_command(label='清空日志记录', command=lambda: init_file.logs())
        file.add_command(label='退出程序', command=Windows.quit)
        self.menu_file.add_cascade(label=name, menu=file)

    def about(self, name):
        about = Menu(self.menu_file, tearoff=False)
        about.add_separator()

        # lambda是为了不让菜单自动触发(command不允许传入参数，否则自动触发），来自博客：https://blog.csdn.net/guge907/article/details/23291763
        about.add_command(label='下载更新', command=lambda: visit(url='https://gitee.com/faith01238/IP/releases'))
        about.add_command(label='关于作者', command=lambda: visit(url='https://www.toutiao.com/c/user/token/MS4wLjABAAAAhkfCgYOJBcLeeCmKBkqlgADR-D_woot1lAXGJFnkEnE/'))
        self.menu_file.add_cascade(label=name, menu=about)
    def card(self, name):
        card_list = Menu(self.menu_file, tearoff=False)
        card_list.add_separator()

        ip_list = init.get_Net_list() #获取网卡信息
        for id in ip_list:
            for card in ip_list[id]:
                text = '[' + str(id) + '] ->' + '[ ' + card + ' ]' + '->' + '[' + ip_list[id][card] + ']'
            # lambda是为了不让菜单自动触发(command不允许传入参数，否则自动触发），来自博客：https://blog.csdn.net/guge907/article/details/23291763
                card_list.add_command(label=text)
        self.menu_file.add_cascade(label=name, menu=card_list)
def update_menu():
    # 添加菜单
    init_menu = add_menu()

    init_menu.file(name='文件')

    init_menu.pr(name='设置')

    init_menu.card(name='网卡')

    init_menu.about(name='关于')

update_menu()

##添加按钮
bt = Buttons(windows=Windows)
#添加功能按钮
bt.fun()

bt.parts(name='访问百度', url='https://baidu.com/', x=20, y=260)
bt.parts(name='百度开发', url='https://kaifa.baidu.com/', x=170, y=260)
bt.parts(name='今日头条', url='https://www.toutiao.com/c/user/token/MS4wLjABAAAAhkfCgYOJBcLeeCmKBkqlgADR-D_woot1lAXGJFnkEnE/', x=320, y=260)
bt.parts(name='CSDN', url='https://blog.csdn.net/qq_36154886/article/details/109211712', x=470, y=260)


bt.parts(name='博客园', url='https://www.cnblogs.com/liuyi778/p/12846511.html', x=20, y=300)
bt.parts(name='Bug反馈', url='https://jq.qq.com/?_wv=1027&k=vC8XNViw', x=170, y=300)
bt.parts(name='赏点', url='https://gitee.com/faith01238/IP#5%E8%B5%8F%E7%82%B9', x=320, y=300)
bt.parts(name='使用教程', url='https://www.toutiao.com/i6923054987920556558', x=470, y=300)







Windows.mainloop()
